package com.ai.module.mj.service.impl;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.ai.common.BaseResp;
import com.ai.module.mj.constant.enums.BlendDimensions;
import com.ai.module.mj.constant.stringConstant.ReturnCode;
import com.ai.module.mj.domain.DiscordAccount;
import com.ai.module.mj.domain.bo.MJDataUrl;
import com.ai.module.mj.helper.SpringContextHolder;
import com.ai.module.mj.service.DiscordService;
import com.ai.module.mj.util.DiscordURLHelper;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.Map;

@Slf4j
public class DiscordServiceImpl implements DiscordService {
	private static final String DEFAULT_SESSION_ID = "90906d506c896471abdccdbce5f364ec";

	private final DiscordAccount account;
	private final Map<String, String> paramsMap;
	private final RestTemplate restTemplate;
	private final DiscordURLHelper discordURLHelper;

	// 任务状态
	private final String discordInteractionUrl;
	// 文件上传 链接
	private final String discordAttachmentUrl;
	// 发送图片
	private final String discordMessageUrl;

	public DiscordServiceImpl(DiscordAccount account, RestTemplate restTemplate, Map<String, String> paramsMap) {
		this.account = account;
		this.restTemplate = restTemplate;
		this.discordURLHelper = SpringContextHolder.getApplicationContext().getBean(DiscordURLHelper.class);
		this.paramsMap = paramsMap;
		String discordServer = this.discordURLHelper.getServer();
		this.discordInteractionUrl = discordServer + "/api/v9/interactions";
		this.discordAttachmentUrl = discordServer + "/api/v9/channels/" + account.getChannelId() + "/attachments";
		this.discordMessageUrl = discordServer + "/api/v9/channels/" + account.getChannelId() + "/messages";
	}

	@Override
	public BaseResp<Void> imagine(String prompt, String nonce) {
		String paramsStr = replaceInteractionParams(this.paramsMap.get("imagine"), nonce);
		JSONObject params = JSONObject.parse(paramsStr);
		params.getJSONObject("data").getJSONArray("options").getJSONObject(0)
				.put("value", prompt);
		return postJsonAndCheckStatus(params.toString());
	}

	@Override
	public BaseResp<Void> upscale(String messageId, int index, String messageHash, int messageFlags, String nonce) {
		String upscale = this.paramsMap.get("upscale");
		String jsonParam = replaceInteractionParams(upscale, nonce);
		String paramsStr = jsonParam
				.replace("$message_id", messageId)
				.replace("$index", String.valueOf(index))
				.replace("$message_hash", messageHash);
		JSONObject json = JSONObject.parse(paramsStr);
		json.put("message_flags", messageFlags);
		paramsStr = json.toString();
		return postJsonAndCheckStatus(paramsStr);
	}

	@Override
	public BaseResp<Void> variation(String messageId, int index, String messageHash, int messageFlags, String nonce) {
		String paramsStr = replaceInteractionParams(this.paramsMap.get("variation"), nonce)
				.replace("$message_id", messageId)
				.replace("$index", String.valueOf(index))
				.replace("$message_hash", messageHash);
		JSONObject parse = JSONObject.parse(paramsStr);
		 parse.put("message_flags", messageFlags);

		return postJsonAndCheckStatus(parse.toString());
	}

	@Override
	public BaseResp<Void> reroll(String messageId, String messageHash, int messageFlags, String nonce) {
		String paramsStr = replaceInteractionParams(this.paramsMap.get("reroll"), nonce)
				.replace("$message_id", messageId)
				.replace("$message_hash", messageHash);
		paramsStr = JSONObject.parse(paramsStr).put("message_flags", messageFlags).toString();
		return postJsonAndCheckStatus(paramsStr);
	}

	@Override
	public BaseResp<Void> describe(String finalFileName, String nonce) {
		String fileName = CharSequenceUtil.subAfter(finalFileName, "/", true);
		String paramsStr = replaceInteractionParams(this.paramsMap.get("describe_link"), nonce)
				.replace("$url", finalFileName)
				.replace("$file_name", fileName)
				.replace("$final_file_name", finalFileName);
		return postJsonAndCheckStatus(paramsStr);
	}

	@SneakyThrows
	@Override
	public BaseResp<Void> blend(List<String> finalFileNames, BlendDimensions dimensions, String nonce) {
		String paramsStr = replaceInteractionParams(this.paramsMap.get("blend"), nonce);
		JSONObject params = JSONObject.parse(paramsStr);
		JSONArray options = params.getJSONObject("data").getJSONArray("options");
		JSONArray attachments = params.getJSONObject("data").getJSONArray("attachments");
		for (int i = 0; i < finalFileNames.size(); i++) {
			String finalFileName = finalFileNames.get(i);
			String fileName = CharSequenceUtil.subAfter(finalFileName, "/", true);
			JSONObject attachment = new JSONObject();
					attachment.put("id", String.valueOf(i));
					attachment.put("filename", fileName);
					attachment.put("uploaded_filename", finalFileName);
			attachments.add(attachment);
			JSONObject option = new JSONObject();
					option.put("type", 11);
					option.put("name", "image" + (i + 1));
					option.put("value", i);
			options.add(option);
		}
		JSONObject json = new JSONObject();
		json.put("type", 3);
		json.put("name", "dimensions");
		json.put("value", "--ar " + dimensions.getValue());
		options.add(json);
		return postJsonAndCheckStatus(params.toString());
	}

	private String replaceInteractionParams(String paramsStr, String nonce) {
		return paramsStr
				.replace("$guild_id", this.account.getGuildId())
				.replace("$channel_id", this.account.getChannelId())
				.replace("$session_id", DEFAULT_SESSION_ID)
				.replace("$nonce", nonce);
	}

	@Override
	public BaseResp<String> upload(String fileName, MJDataUrl dataUrl) {
		try {
			JSONObject fileObj = new JSONObject();
			fileObj.put("filename", fileName);
			fileObj.put("file_size", dataUrl.getData().length);
			fileObj.put("id", "0");
			JSONObject params = new JSONObject();
			JSONArray fileArray = new JSONArray();
			fileArray.add(fileObj);
			params.put("files",fileArray );
			ResponseEntity<String> responseEntity = postJson(this.discordAttachmentUrl, params.toString());
			if (responseEntity.getStatusCode() != HttpStatus.OK) {
				log.error("上传图片到discord失败, status: {}, msg: {}", responseEntity.getStatusCodeValue(), responseEntity.getBody());
				return BaseResp.of(ReturnCode.VALIDATION_ERROR, "上传图片到discord失败");
			}
			JSONObject json = JSONObject.parse(responseEntity.getBody());
			assert json != null;
			JSONArray array = json.getJSONArray("attachments");
			if (array.size() == 0) {
				log.error("上传图片到discord失败, status: {}, msg: {}", responseEntity.getStatusCodeValue(), responseEntity.getBody());
				return BaseResp.of(ReturnCode.VALIDATION_ERROR, "上传图片到discord失败");
			}
			String uploadUrl = array.getJSONObject(0).getString("upload_url");
			String uploadFilename = array.getJSONObject(0).getString("upload_filename");
			putFile(uploadUrl, dataUrl);
			return BaseResp.success(uploadFilename);
		} catch (Exception e) {
			log.error("上传图片到discord失败", e);
			e.printStackTrace();
			return BaseResp.of(ReturnCode.FAILURE, "上传图片到discord失败");
		}
	}

	@Override
	@SneakyThrows
	public BaseResp<String> sendImageMessage(String content, String finalFileName) {
		String fileName = CharSequenceUtil.subAfter(finalFileName, "/", true);
		String paramsStr = this.paramsMap.get("message").replace("$content", content)
				.replace("$channel_id", this.account.getChannelId())
				.replace("$file_name", fileName)
				.replace("$final_file_name", finalFileName);
		ResponseEntity<String> responseEntity = postJson(this.discordMessageUrl, paramsStr);
		if (responseEntity.getStatusCode() != HttpStatus.OK) {
			log.error("发送图片消息到discord失败, status: {}, msg: {}", responseEntity.getStatusCodeValue(), responseEntity.getBody());
			return BaseResp.of(ReturnCode.VALIDATION_ERROR, "发送图片消息到discord失败");
		}
		JSONObject result = JSONObject.parse(responseEntity.getBody());
		JSONArray attachments = result.getJSONArray("attachments");
		if (!CollectionUtil.isEmpty(attachments)) {
			return BaseResp.success(attachments.getJSONObject(0).getString("url"));
		}
		return BaseResp.failure("发送图片消息到discord失败: 图片不存在");
	}

	private void putFile(String uploadUrl, MJDataUrl dataUrl) {
		uploadUrl = this.discordURLHelper.getDiscordUploadUrl(uploadUrl);
		HttpHeaders headers = new HttpHeaders();
		headers.add("User-Agent", this.account.getUserAgent());
		headers.setContentType(MediaType.valueOf(dataUrl.getMimeType()));
		headers.setContentLength(dataUrl.getData().length);
		HttpEntity<byte[]> requestEntity = new HttpEntity<>(dataUrl.getData(), headers);
		this.restTemplate.put(uploadUrl, requestEntity);
	}

	private ResponseEntity<String> postJson(String paramsStr) {
		return postJson(this.discordInteractionUrl, paramsStr);
	}

	private ResponseEntity<String> postJson(String url, String paramsStr) {
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON);
		headers.setBearerAuth(this.account.getUserToken());
//		headers.set(HttpHeaders.AUTHORIZATION, this.account.getUserToken());
		headers.set("User-Agent", this.account.getUserAgent());
		HttpEntity<String> httpEntity = new HttpEntity<>(paramsStr, headers);
		return this.restTemplate.postForEntity(url, httpEntity, String.class);
	}

	private BaseResp<Void> postJsonAndCheckStatus(String paramsStr) {
		try {
			ResponseEntity<String> responseEntity = postJson(paramsStr);
			if (responseEntity.getStatusCode() == HttpStatus.NO_CONTENT) {
				return BaseResp.success();
			}

			return BaseResp.of(responseEntity.getStatusCodeValue(), CharSequenceUtil.sub(responseEntity.getBody(), 0, 100));
		} catch (HttpStatusCodeException e) {
			return convertHttpStatusCodeException(e);
		}
	}

	private BaseResp<Void> convertHttpStatusCodeException(HttpStatusCodeException e) {
		try {
			JSONObject error =  JSONObject.parse(e.getResponseBodyAsString());
			return BaseResp.of((Integer) error.getOrDefault("code", e.getRawStatusCode()), error.getString("message"));
		} catch (Exception je) {
			je.printStackTrace();
			log.error("画图任务失败[{}]", je.getMessage());
			return BaseResp.of(e.getRawStatusCode(), CharSequenceUtil.sub(e.getMessage(), 0, 100));
		}
	}
}
